We design and build clean, simple, and easy to use web applications. Hire us for your next project…

Passenger preference pane v1.2

Eloy Duran, 03 Dec 2008, 17:47 in ruby on rails, tools, and releases (edit).

X-Mas came early this year ;-)

Checkout the changelog:

  • Rack support. Thanks to Kematzy for the original suggestion/work.
  • Fixed “issue” people had with the default non-vhost based directory not working anymore. Thanks to “spike” for debugging this.
  • Made the vhost config file extension configurable as well. Patch by Felipe Mathies.
  • Backported RubyCocoa + Ruby 1.8.7 fix. Patch by Grant Hollingworth.
  • Removed the “Allow mod rewrite rules” checkbox, we found it’s not worth the UI real estate.

Most users probably want to download the “stable” 1.2 release.

If you understand why stable has been quoted, you can track development and contribute on: github.com/alloy/passengerpane

Please report any bugs you may find at: fingertips.lighthouseapp.com/projects/13022

4 comments



Adding development gem dependencies to a rails application.

Eloy Duran, 01 Dec 2008, 16:56 in ruby on rails and testing (edit).

It has been possible to specify gem dependencies for your application for a while. But sometimes when moving to another machine we would complain about there not being a development dependency option for config.gem as there is for gems. But we hadn’t actually given it any thought…

Today I had some weird test failures, which were related to not having test-spec 0.9 installed on my macbook. Most tests did pass because I did have an older version installed. So after a few minutes of thinking about it, I finally figured out the stupid simple solution. Simply define the dependencies in config/environments/test.rb!


# Development gems:
config.gem 'test-spec', :version => '0.9', :lib => 'test/spec'
config.gem 'mocha', :version => '0.9.3'

This one is probably obvious to some, but as far as I know it has never been actively promoted. And it’s just too easy to think of ;-)

2 comments



Unichars 0.2 released

Manfred Stienstra, 24 Nov 2008, 22:09 in ruby on rails and releases (edit).

Unichars is a simple wrapper around Glib2 Unicode functions. You can use it to speed up certain methods on Unicode string. Currently supported are: upcase, downcase, reverse, and size. The cool thing about it is that it works seamlessly with ActiveSupport::Multibyte and it works great without ActiveSupport::Multibyte.

I guess Unichars is not a very exiting name like God, Vlad the Deployer, or Gerard Joling but I guess I’m not that kind of a guy.

You can install Unichars with Rubygems:

$ gem install unichars

Or you can fetch it from Github:

$ git clone git://github.com/Manfred/unichars.git
$ cd unichars
$ rake gem:install

With Rails

The examples in the README tell you how to use Unichars with Rails 2.1 or newer. I’ll just re-iterate how it’s done.

First you make sure you load the library, the easiest way to do this is with config.gem in environment.rb:

config.gem 'unichars'

Or when you dislike gems, you can just require it:

require 'unichars'

When you’re not using config.gem, you have to make sure ActiveSupport is loaded before Unichars, otherwise the Rails integration won’t magically work.

After that you have to tell ActiveSupport::Multibyte to use the Unichars class as proxy class. You can do that in an initializer or at the end of your environment.rb. I would recommend doing it in config/initializers/unichars.rb.

ActiveSupport::Multibyte.proxy_class = Unichars

Now all of Rails will automatically use the Unichars character proxy, you can also use it yourself:

'青山'.mb_chars.reverse #=> '山青'

Without Rails, but with ActiveSupport

require 'activesupport'
require 'unichars'
ActiveSupport::Multibyte.proxy_class = Unichars
$KCODE = 'u'

Other than that it’s similar to Rails:

'Sluß'.mb_chars.upcase #=> 'SLUSS'

A good time to talk about LC_CTYPE real quick. Note that Glib2 picks that up from your environment, so your results may vary depending on what it’s set too.

Without training wheels

require 'unichars'

If you don’t use ActiveSupport, you can still use Unichars because it comes with a light version of the Chars proxy. You will have to wire it yourself though:

class String
  def mb_chars
    Unichars.new(self)
  end
end

'Copy-®'.mb_chars.size #=> 6

Without anything

Finally, you can just use the Glib2 wrapper and roll your own solution:

require 'glib'
Glib.utf8_upcase('Comme des Garçons').upcase #=> 'COMME DES GARÇONS'

Questions?

If you have any questions or issues, please use the Github Wiki Wiki as much as possible. If you want to discuss anything you can find me on Freenode in #rails-contrib. Have fun with Unichars!

2 comments

Free result after using ActiveRecord::Base.connection.execute

Manfred Stienstra, 24 Nov 2008, 18:47 in ruby on rails (edit).

Just a quick warning: when you use ActiveRecord::Base.connection.execute you get a Mysql::Result instance, this is a very thin wrapper around the actual result returned from libmysqlclient. This means you have to free the memory of the result table, failing to do so will result in erratic query times because somewhere a garbage collector will do it for you.

class Person < ActiveRecord::Base
  # Returns the last name of someone given the ID
  def self.last_name(id)
    result = connection.execute("SELECT last_name FROM people" +
      " WHERE id = %d" % id)
    last_name = result.fetch_row.first
    result.free
    last_name
  end
end

When you want to select a number of entire rows, you can use the already safe select_rows.

4 comments



Testing randomness

Manfred Stienstra, 30 Sep 2008, 10:51 in ruby on rails and testing (edit).

The problem you run into when trying to write unit tests for methods using random data is that you can’t predict what random data it’s going to use. The idea behind a unit test is that you control the input and test whether the output is as you expected, so that’s a problem.

Let’s assume you have a token generator like this:

module Token
  DEFAULT_LENGTH = 8
  CHARACTERS = ('a'..'f').to_a + ('0'..'9').to_a
  
  def self.generate(options={})
    srand
    options[:length] = DEFAULT_LENGTH if options[:length].nil?
    (1..options[:length]).map { CHARACTERS.rand }.join
  end
end

Now you can write tests to see if it generates tokens of the correct length, doesn’t generate homogeneous tokens and doesn’t generate the same token a number of times in a row. Unfortunately these tests could succeed a number of times and suddenly fail for no apparent reason. Usually this happens when you’re on vacation and one of your collegues is running the test on some weird architecture leaving them baffled.

The solution is to make Array#rand predictable by stubbing out the normal implementation:

class Array
  def round_robin_rand_list=(list)
    @round_robin_rand_list = list
  end
  
  def rand
    current = @round_robin_rand_list.shift
    @round_robin_rand_list.push(current)
    current
  end
end

Now you can write a test that checks the output, because you control what the random method returns:

class TokenTest < Test::Unit::TestCase
  def test_token_generate
    Token::CHARACTERS.round_robin_rand_list = [0, 1, 14]
    assert_equal 'ab8ab8ab', Token.generate
    Token::CHARACTERS.round_robin_rand_list = [7, 12, 15, 3]
    assert_equal '169d169d', Token.generate
  end
end

Obviously you need to clean up after yourself in tests so you can’t just redefine methods. It would be really cool if we could do something like this:

class TokenTest < Test::Unit::TestCase
  def test_token_generate
    Token::CHARACTERS.define_round_robin_method(:rand, ['a', 'b', '8'])
    assert_equal 'ab8ab8ab', Token.generate
    Token::CHARACTERS.undefine_round_robin_method(:rand)
  end
end

We’ve implemented this as round_robin_method.rb, complete with its own testsuite. You could go even further and hook it into TestUnit to automatically undefine the round robin methods after a test has run. We’ll leave that as an exercise for another day.

2 comments

ActiveSupport::Multibyte Updated

Manfred Stienstra, 23 Sep 2008, 13:47 in ruby on rails, unicode, and releases (edit).

Yesterday Michael Koziarski merged the updated version of ActiveSupport::Multibyte into Rails. The initial reason for the update was Ruby 1.9 compatibility but it turned into a complete overhaul. Not just the code, but also the documentation was revised.

For most people the only noticeable change is the move from String#chars to String#mb_chars. People relying heavily on ActiveSupport::Multibyte probably want to read on.

String#chars renamed to String#mb_chars

One of the initial reasons to use a proxy to access characters back in 2006 was to make Rails future proof in case Ruby got some kind of Unicode support on String. Unfortunately Matz decided to use String#chars for one of these features so we had to change the method name. People running on Ruby <= 1.8.6 will get a nice deprecation warning.

String#mb_chars now returns a proxy on Ruby 1.8 and returns self on Ruby 1.9.

Note that the Ruby 1.9 String class does not implement methods like String#normalize. We’re still trying to figure out how to approach this limitation. For now, you might want to do:

class String
  def normalize(normalization_form=ActiveSupport::Multibyte.default_normalization_form)
    ActiveSupport::Multibyte::Chars.new(self).normalize(normalization_form)
  end
end

No more automatic tidying of bytes

Multibyte no longer attempts to convert broken encoding in strings to a valid UTF-8. The String#tidy_bytes method still exists if you need this functionality.

Duck-typing aid

Strings are notoriously hard to duck-type because they include Enumerable, which makes them hard to differentiate from Arrays. Rails already had some duck-typing help in place for Date, Time and DateTime. We decided to implement the same thing on String and Chars.

'Bambi and Thumper'.acts_like?(:string) #=> true
'Bambi and Thumper'.mb_chars.acts_like?(:string) #=> true

So if you catch yourself using str.is_a?(String) please consider using acts_like?.

Different way of registering backends

Instead of registering a handler on the Chars class, you now set the proxy_class on ActiveSupport::Multibyte.

ActiveSupport::Multibyte.proxy_class = UTF32Chars

Note that this removes a level of indirection, which speeds up the entire Multibyte implementation quite a bit.

If you’ve implemented your own handler, please look at the implementation of ActiveSupport::Multibyte::Chars on how to convert it to work with the new implementation. In most cases this should be a trivial exercise. Don’t hesitate to contact me if you need help.

Overrideable default normalization form

The default normalization form can now be set on ActiveSupport::Multibyte instead of updating a constant.

ActiveSupport::Multibyte.default_normalization_form = :kd

See ActiveSupport::Multibyte::NORMALIZATIONS_FORMS for valid normalization forms.

1 comment

Passenger preference pane v1.1

Eloy Duran, 19 Sep 2008, 18:10 in ruby on rails, tools, and releases (edit).

To get the latest version, please see the Passenger preference pane page.

Yes yes, it’s update time!

This version comes with important fixes and some requested improvements. I’ll let the changelog speak for itself:

  • Honor custom environments that a user might have set.
  • Fixed problem with restarting Apache. After saving an application Apache should now automatically be restarted. Thanks to Ciarán Walsh.
  • Added support for ServerAlias and add those entries to the hosts db.
  • Reload the applications from disk when the preference pane is brought back to the front. Any changes made to the vhosts from elsewhere will be reflected in the UI.
  • Moved all hardcoded paths into a config module. Added a config for Apache 2 as installed by MacPorts. Thanks to Ciarán Walsh.
  • The host table list was editable. Thanks to Ciarán Walsh.
  • Fixed bugs in parsing custom user defined data in vhosts.
  • Create a tmp dir before touching restart.txt if none exists.
  • Replace underscores with hyphens in hostnames. Thanks to Bryan Liles.

Most users probably want to download the “stable” 1.1 release.

If you understand why stable has been quoted, you can track development and contribute on: github.com/alloy/passengerpane

Please report any bugs you may find at: fingertips.lighthouseapp.com/projects/13022

10 comments



Berlin 2008

Manfred Stienstra, 30 Aug 2008, 21:44 in ruby on rails and meetings (edit).

Eloy and I will be in Berlin Monday through Thursday. We’re not attending RailsConf, but we are meeting up with a small detachment of European Caboosers to hack on projects and have a good time. We’ve rented a big apartment near the conference which we’re using as our HQ.

If you want to meet up or want to discuss anything related to projects we’re working on let us know and we’ll hook up.

5 comments

Aliasing dangerous methods in your tests

Manfred Stienstra, 21 Aug 2008, 11:29 in ruby on rails and testing (edit).

Lately we’ve been aliasing dangerous methods in our tests to make sure we don’t accidentally break something. It’s not very nice when tests delete your favorite episode of Sesame Street or add a MySQL user, so we had a lot of snippets like this laying around:


module Kernel
  mattr_accessor :allow_system
  self.allow_system = false

  alias original_system system

  def self.system(*args)
    if allow_system
      original_system(*args)
    else
      raise RuntimeError, "You're trying to do a system call, which is probably not a very good idea in a test."
    end
  end
end

We refactored it and now we specify the same thing like this:


ActiveResource::Connection.add_allow_switch :request
Kernel.add_allow_switch :system, :default => true

The code is currently included in on_test_spec, our plugin for writing Rails specs on test/spec.

No comments yet



Buying an Openmoko Neo FreeRunner

Manfred Stienstra, 04 Jul 2008, 13:56 in web and broken (edit).

Today Openmoko released the first consumer phone running the Openmoko platform, called Neo FreeRunner. According to a post on Slashdot it’s only sold to people in the United States, so I wanted to see what happened if I tried to buy one.

I started out on the openmoko.com frontpage. There is a big green box inviting me to click on it, so I do that.

Nice, a picture and specifications on one page. That’s really helpful because those are the things I look for on a product page. Now I want to buy it, so I click ‘BUY IT NOW’.

Apparently ‘now’ is a relative term. My guess is that ordering one through a distributor near me is going to take longer, so I click ‘BUY FROM THE OPENMOKO STORE’.

Ok, another product page. I notice that the top navigation changed to ‘STORE/DIRECT’, so at least I made it into the store. I notice another ‘BUY IT NOW’ link, so I click that.

Huh, what’s this, another product page? I check the top navigation to make sure I’m still in the store. I guess I need to scroll down now.

Ah, finally, an ‘ADD TO CART’ link. That usually means we’re almost there.

Unfortunately the store seems to be broken. The website informs us that someone apparently forgot to pass the variant ID, what a bummer. Maybe I should try the search next time.

4 comments

...continue reading in the archive for July 2008